#include "shtunel.hxx"
#include <utility.hxx>
#include "autoerconstant.hxx"
#include <curl/curl.h>
#include <myfilelog.hxx>
#include <loghelper.hxx>
#include <cassert>

SHTunel::SHTunel(const string& host, const string& method, FileLog* filelog)
:host(host),
method(method),
curlRaw(NULL),
curlRawInUse(),
curlError(),
inUse(false),
generalHeaders(),
_filelog(filelog)
{
	bool inited = false;
	while (!inited)
	{
		inited = Init();			
	}
}

SHTunel::~SHTunel()
{
	int counter = 10;
	while (inUse && --counter>0)
		Thread::Sleep(1);

	if (curlRaw)
	{
		LOGDEBUG("SHTunel::~SHTunel curlRaw #"<<curlRaw<<" cleaned up!");

		curl_easy_cleanup(curlRaw);
		curlRaw = NULL;
	}
}

CURL* SHTunel::GetCurl(void) const
{
	return curlRaw;
}

const char* SHTunel::GetCurlError(void) const
{
	return curlError;
}

bool SHTunel::SendLeftHeaders(void)
{
	Utility::Lock lock(&curlRawInUse);

	string toSend;
	string header = NextHItem();

	while (!header.empty())
	{
		toSend += header;
		header = NextHItem();
	}

	if (!toSend.empty()	&& !SendHItem(toSend))
		return false;

	return true;

}
bool SHTunel::KeepAlive(void)
{
	if (inUse)
		return true;

	Utility::Lock lock(&curlRawInUse);

	assert(!inUse);

	LOGDEBUG("SHTunel::KeepAlive curlRaw #"<<curlRaw<<" started...");

	string hItem = NextHItem();
	if (hItem.empty())
	{
		LOGDEBUG("SHTunel::KeepAlive curlRaw #"<<curlRaw<<" no header left.");
		return false;
	}

	if (!SendHItem(hItem))
	{
		LOGDEBUG("SHTunel::KeepAlive curlRaw #"<<curlRaw<<" kept alive failed.");
		return false;
	}

	LOGDEBUG("SHTunel::KeepAlive curlRaw #"<<curlRaw<<" kept alive successfully!");

	return true;
}

bool SHTunel::SetInUse(void)
{
	if (inUse)
		return false;

	Utility::Lock lock(&curlRawInUse);

	if (inUse)
		return false;

	if (!SendLeftHeaders())
	{
		LOGWARNING("SHTunel::SetInUse curlRaw #"<<curlRaw<<" failed at SendLeftHeaders.");
		return false;
	}

	inUse = true;

	LOGDEBUG("SHTunel::SetInUse curlRaw #"<<curlRaw<<" set inuse.");

	return true;
}

bool SHTunel::Reset(void)
{
	Utility::Lock lock(&curlRawInUse);

	generalHeaders.clear();	// so that start from first header item.
	inUse = false;

	return Init();
}

bool SHTunel::Init(void)
{
	if (inUse)
		return false;

	Utility::Lock lock(&curlRawInUse);

	if (inUse)
		return false;

	if (curlRaw)
	{
		LOGDEBUG("SHTunel::Init curlRaw #"<<curlRaw<<" cleaned up!");

		curl_easy_cleanup(curlRaw);
		curlRaw = NULL;
	}

	memset(curlError, 0x00, CURL_ERROR_SIZE);

	curlRaw = curl_easy_init();
	if (!curlRaw)
		return false;

	if (!(	CURLE_OK == curl_easy_setopt(curlRaw, CURLOPT_VERBOSE, 1)
		&&	CURLE_OK == curl_easy_setopt(curlRaw, CURLOPT_ERRORBUFFER, curlError)
		&&	CURLE_OK == curl_easy_setopt(curlRaw, CURLOPT_URL, host.c_str())
		&&	CURLE_OK == curl_easy_setopt(curlRaw, CURLOPT_CONNECT_ONLY, 1)
		&&	CURLE_OK == curl_easy_perform(curlRaw)))
	{
		curl_easy_cleanup(curlRaw);
		curlRaw = NULL;
		return false;
	}

	const string hItem = method + " HTTP/1.1\r\n"
		+ "Host: " + host + "\r\n"
		+ "User-Agent: " + Meta::UserAgent + "\r\n";
	if (!SendHItem(hItem))
	{
		curl_easy_cleanup(curlRaw);
		curlRaw = NULL;
		return false;
	}

	bool ret = KeepAlive();

	if (ret)
		LOGDEBUG("SHTunel::Init curlRaw #"<<curlRaw<<" Inited successfully!");
	else
		LOGWARNING("SHTunel::Init curlRaw #"<<curlRaw<<" Inited failed!!");

	/* Ensure the first sending succeeded. */
	return ret;
}

bool SHTunel::SendHItem(const string& hItem)
{
	const int VL = 20;
	CURLcode res = CURLE_OK;
	size_t n = 0;

	Utility::Lock lock(&curlRawInUse);

	if (Utility::IsReadyForWrite(curlRaw, VL) <= 0)
	{
		LOGWARNING("SHTunel::SendHItem failed !IsReadyForWrite!!");
		return false;
	}

	res = curl_easy_send(curlRaw, hItem.c_str(), hItem.length(), &n);

	if (CURLE_OK != res || hItem.length() != n)
	{
		LOGWARNING("SHTunel::SendHItem curlRaw #"<<curlRaw<<" failed with res="<<res<<", n="<<n<<","<<curlError);
		return false;
	}

	return true;
}
string SHTunel::NextHItem(void)
{
	if (generalHeaders.empty())
	{
		generalHeaders["Connection"] = "keep-alive";
		generalHeaders["Cache-Control"] = "max-age=0";
		generalHeaders["Content-Type"] = "application/x-www-form-urlencoded";
		generalHeaders["Accept"] = "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8";
		generalHeaders["Accept-Encoding"] = "gzip,deflate,sdch";
		generalHeaders["Accept-Language"] = "zh-CN,zh;q=0.8";
		//generalHeaders["Accept-Charset"] = "GBK,utf-8;q=0.7,*;q=0.3";
		generalHeaders["Pragma"] = "no-cache";
		generalHeaders["Expect"] = "100-continue"; 
		iterGeneralHeaders = generalHeaders.begin();
	}

	string result;
	if (iterGeneralHeaders == generalHeaders.end())
	{
		return result;
	}

	result = iterGeneralHeaders->first + ": " + iterGeneralHeaders->second + "\r\n";

	++iterGeneralHeaders;

	return result;
}